﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public static class TextureExtensions
{
    public static Texture2D ToTexture2D(this Texture texture)
    {
        return Texture2D.CreateExternalTexture(
            texture.width,
            texture.height,
            TextureFormat.RGB24,
            false, false,
            texture.GetNativeTexturePtr());
    }

    public static Texture2D ToTexture2D(this RenderTexture rTex, int width, int height, TextureFormat format)
    {
        Texture2D tex = new Texture2D(width, height, format, false);
        RenderTexture.active = rTex;
        tex.ReadPixels(new Rect(0, 0, rTex.width, rTex.height), 0, 0);
        tex.Apply();
        return tex;
    }

    public static Texture2D Cropping(Texture2D original, Vector2 leftUp, Vector2 rightBottom)
    {
        int width = (int) ((rightBottom.x - leftUp.x) * original.width);
        int height = (int) ((rightBottom.y - leftUp.y) * original.height);

        Texture2D destTex = new Texture2D(width, height, original.format, true);

        Color[] destPix = new Color[width * height];
        // float x_start = Mathf.Min(leftUp.x, rightBottom.x);
        // float x_end = Mathf.Max(leftUp.x, rightBottom.x);
        // float y_start = Mathf.Min(leftUp.y, rightBottom.y);
        // float y_end = Mathf.Max(leftUp.y, rightBottom.y); 
        float x_start = leftUp.x;
        float x_end = rightBottom.x;
        float y_start = leftUp.y;
        float y_end = rightBottom.y;
        int y = 0;
        while (y < destTex.height)
        {
            int x = 0;
            while (x < destTex.width)
            {
                float xFrac = x * 1.0F / (destTex.width - 1);
                float yFrac = y * 1.0F / (destTex.height - 1);
                float warpXFrac = x_start + xFrac * (x_end - x_start);
                float warpYFrac = y_start + yFrac * (y_end - y_start);
                Color c = original.GetPixelBilinear(warpXFrac, warpYFrac);
                if (warpXFrac < 0 || warpXFrac > 1 || warpYFrac < 0 || warpYFrac > 1)
                    c = Color.white;
                destPix[y * destTex.width + x] = c;
                x++;
            }

            y++;
        }

        destTex.SetPixels(destPix);
        destTex.Apply();

        return destTex;
    }

    public static Texture2D Cropping(Texture2D original, Rect rect)
    {
        return Cropping(original, new Vector2(rect.xMin / rect.width, rect.yMin / rect.height),
            new Vector2(rect.xMax / rect.width, rect.yMax / rect.height));
    }

    /// <summary>
    /// resize Texture2D
    /// </summary>
    /// <param name="source"></param>
    /// <param name="targetWidth"></param>
    /// <param name="targetHeight"></param>
    /// <returns></returns>
    public static Texture2D ResampleAndCrop(Texture2D source, int targetWidth, int targetHeight)
    {
        int sourceWidth = source.width;
        int sourceHeight = source.height;
        float sourceAspect = (float) sourceWidth / sourceHeight;
        float targetAspect = (float) targetWidth / targetHeight;
        int xOffset = 0;
        int yOffset = 0;
        float factor = 1;
        if (sourceAspect > targetAspect)
        {
            // crop width
            factor = (float) targetHeight / sourceHeight;
            xOffset = (int) ((sourceWidth - sourceHeight * targetAspect) * 0.5f);
        }
        else
        {
            // crop height
            factor = (float) targetWidth / sourceWidth;
            yOffset = (int) ((sourceHeight - sourceWidth / targetAspect) * 0.5f);
        }

        Color32[] data = source.GetPixels32();
        Color32[] data2 = new Color32[targetWidth * targetHeight];
        for (int y = 0; y < targetHeight; y++)
        {
            for (int x = 0; x < targetWidth; x++)
            {
                var p = new Vector2(Mathf.Clamp(xOffset + x / factor, 0, sourceWidth - 1),
                    Mathf.Clamp(yOffset + y / factor, 0, sourceHeight - 1));
                // bilinear filtering
                var c11 = data[Mathf.FloorToInt(p.x) + sourceWidth * (Mathf.FloorToInt(p.y))];
                var c12 = data[Mathf.FloorToInt(p.x) + sourceWidth * (Mathf.CeilToInt(p.y))];
                var c21 = data[Mathf.CeilToInt(p.x) + sourceWidth * (Mathf.FloorToInt(p.y))];
                var c22 = data[Mathf.CeilToInt(p.x) + sourceWidth * (Mathf.CeilToInt(p.y))];
                var f = new Vector2(Mathf.Repeat(p.x, 1f), Mathf.Repeat(p.y, 1f));
                data2[x + y * targetWidth] = Color.Lerp(Color.Lerp(c11, c12, p.y), Color.Lerp(c21, c22, p.y), p.x);
            }
        }

        var tex = new Texture2D(targetWidth, targetHeight);
        tex.SetPixels32(data2);
        tex.Apply(true);
        return tex;
    }

    public static Texture2D Cropping(this Texture2D original, int x, int y, int width, int height, bool isTop = false)
    {
        int coordinateY = isTop ? y - height : y;
        if (x < 0 || coordinateY < 0)
        {
            Debug.LogError("Cropping Error：x or y less than 0." + $" x:{x} y:{y} width:{width} height:{height}");
            return null;
        }

        Texture2D texture2D = new Texture2D(width, height);
        Color[] c = original.GetPixels(x, coordinateY, width, height);
        texture2D.SetPixels(c);
        texture2D.Apply();
        return texture2D;
    }

    public static Texture2D Flip(this Texture2D original, bool upSideDown = true)
    {
        Texture2D flipped = new Texture2D(original.width, original.height,original.format,false);

        int xN = original.width;
        int yN = original.height;

        for (int i = 0; i < xN; i++)
        {
            for (int j = 0; j < yN; j++)
            {
                if (upSideDown)
                {
                    //flipped.SetPixel(j, xN - i - 1, original.GetPixel(j, i));
                    flipped.SetPixel(i, yN - j - 1, original.GetPixel(i,j));
                }
                else
                {
                    flipped.SetPixel(xN - i - 1, j, original.GetPixel(i, j));
                }
            }
        }

        flipped.Apply();

        return flipped;
    }

    public static Texture2D RotationLeft90(this Texture2D original)
    {
      
        Color32[] src = original.GetPixels32();
        Color32[] srcNew = new Color32[src.LongLength];
        int originalWidth = original.width;
        int originalHeight = original.height;
        Texture2D desTex = new Texture2D(originalHeight, originalWidth);
        for (int i = 0; i < originalWidth; i++)
        {
            for (int j = 0; j < originalHeight; j++)
            {
                srcNew[i * originalHeight + j] = src[(originalHeight - 1 - j) * originalWidth + i];
            }
        }
        desTex.SetPixels32(srcNew);
        desTex.Apply();
        return desTex;
    }
}